
namespace Sharepoint.ImportExport.Extractor.Common
{
    /// <summary>
    /// This is a helper class with common routines
    /// </summary>

    using System;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Web;
    using Microsoft.SharePoint;
    using SharePoint.ImportExport.Core;

    /// <summary>
    /// This is a helper class
    /// </summary>
    public class SharedCode
    {
        /// <summary>
        /// Replaces in a case sensitive way the searchstring with the replaceWith string
        /// </summary>
        /// <param name="input">data string</param>
        /// <param name="searchString">text to search for</param>
        /// <param name="replaceWith">replacement</param>
        /// <param name="replaced">boolean flag</param>
        /// <returns>Result from replacement operation</returns>
        public static string ManagedReplace(string input, string searchString, string replaceWith, out bool replaced)
        {
            replaced = false;
            string result = input;
            int iPos = input.ToLower().IndexOf(searchString.ToLower(System.Globalization.CultureInfo.InvariantCulture));
            if (iPos >= 0)
            {
                result = input.Substring(0, iPos) + replaceWith + input.Substring(iPos + searchString.Length);
                replaced = true;
            }
            return result;
        }

        /// <summary>
        /// This method gets the account name from the userValue string
        /// </summary>
        /// <param name="userValue">Username in format 1#xxxxx</param>
        /// <param name="web">Web to use when looking for the user</param>
        /// <returns>The loginname of the user</returns>
        public static string GetUserValue(string userValue, SPWeb web)
        {
            // there are some cases where this will fail and need special handling
            // 1) Users that have been removed will look like "-1;#"
            // 2) Surveys set to not show who filled them out will look like "***"
            try
            {
                int userId = int.Parse(userValue.Substring(0, userValue.IndexOf(";#")), CultureInfo.InvariantCulture);
                SPUser user = web.SiteUsers.GetByID(userId);
                userValue = user.LoginName;
            }
            catch (SPException sharePointException)
            {
                Logger.Log(ErrorLevel.File, "Could not resolve user : " + userValue + " from web " + web.Title + ". Error : " + sharePointException.Message);
                // keep values that don't contain ";#", empty set the rest
                if (userValue.IndexOf(";#") >= 0)
                {
                    userValue = String.Empty;
                }
            }

            return userValue;
        }
        
        /// <summary>
        /// This checks if the field specified by the fieldName parameter is a default field of the content type specified in the contentTypeName parameter
        /// </summary>
        /// <param name="contentTypeName">Name of the content type to check</param>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        public static bool CheckIfDefaultField(string contentTypeName, string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "ContentType":
                case "ContentTypeId":
                case "Author":
                case "Editor":
                case "Created":
                case "Modified":
                case "ID":
                    {
                        returnValue = true;
                        break;
                    }
            }

            switch (contentTypeName)
            {
                case "Contacts":
                    {
                        returnValue = CheckContacts(fieldName);
                        break;
                    }

                case "Issue":
                    {
                        returnValue = CheckIssues(fieldName);
                        break;
                    }

                case "PictureLibrary":
                    {
                        returnValue = CheckPictures(fieldName);
                        break;
                    }

                case "Tasks":
                    {
                        returnValue = CheckTasks(fieldName);
                        break;
                    }

                case "Events":
                    {
                        returnValue = CheckEvents(fieldName);
                        break;
                    }

                case "DataSources":
                case "Survey":
                    {
                        returnValue = false;
                        break;
                    }

                case "Links":
                    {
                        returnValue = CheckLinks(fieldName);
                        break;
                    }

                case "DiscussionBoard":
                case "Message":
                    {
                        returnValue = CheckDiscussionBoards(fieldName);
                        break;
                    }
                case "Announcements":
                    {
                        returnValue = CheckAnnouncements(fieldName);
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// This method opens a file based on the URL
        /// </summary>
        /// <param name="fileName">File name to be created</param>
        /// <param name="file">SPFile item</param>
        /// <returns>True if the file was successfully saved</returns>
        public static bool SaveFile(string fileName, SPFile file)
        {
            bool returnValue = true;
            if (String.IsNullOrEmpty(fileName))
            {
                return false;
            }

            try
            {

                using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    Logger.Log(ErrorLevel.File, "FileStream created");
             
                    byte[] bytes = file.OpenBinary();
                    Logger.Log(ErrorLevel.File, "file.OpenBinary, length "+ bytes.Length.ToString());
                    fs.Write(bytes, 0, bytes.Length);
                    Logger.Log(ErrorLevel.File, "FileStream Write");
                    fs.Flush();
                    Logger.Log(ErrorLevel.File, "FileStream Flush");
                    fs.Close();
                    Logger.Log(ErrorLevel.File, "FileStream Close");
                }
            }
            catch (IOException ioEx)
            {
                Logger.Log(ErrorLevel.Error, "File saving failed, IO error: " + ioEx.Message);
                //// if that fails, try FPRPC - needed for certain file types (like .dwt)
                string absSite = file.ParentFolder.ParentWeb.Url;
                string folder = file.ParentFolder.ToString();
                if (!FpRpcGetDocument(absSite, folder, file.Name, fileName))
                {
                    returnValue = false;
                }
            }
            catch (Exception Ex)
            {
                
                if (Ex.InnerException != null)
                {
                    Logger.Log(ErrorLevel.Information, "File saving failed, error: " + Ex.InnerException.GetType().ToString());
                }
                Logger.Log(ErrorLevel.Error, "File saving failed, error: " + Ex.Message);
                //// if that fails, try FPRPC - needed for certain file types (like .dwt)
                string absSite = file.ParentFolder.ParentWeb.Url;
                string folder = file.ParentFolder.ToString();
                if (!FpRpcGetDocument(absSite, folder, file.Name, fileName))
                {
                    returnValue = false;
                }
            }
            finally
            {
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Discussionboard
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckDiscussionBoards(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "DiscussionTitle":
                case "ReplyNoGif":
                case "ThreadingControls":
                case "IndentLevel":
                case "Indentation":
                case "StatusBar":
                case "BodyAndMore":
                case "Threading":
                case "PersonImage":
                case "PersonViewMinimal":
                case "LinkDiscussionTitleNoMenu":
                case "LinkDiscussionTitle":
                case "ItemChildCount":
                case "DiscussionLastUpdated":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Pictures
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckPictures(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "ImageWidth":
                case "ImageHeight":
                case "ImageCreateDate":
                case "Description":
                case "FileType":
                case "ImageSize":
                case "EncodedAbsThumbnailUrl":
                case "EncodedAbsWebImgUrl":
                case "SelectedFlag":
                case "NameOrTitle":
                case "RequiredField":
                case "Keywords":
                case "Thumbnail":
                case "Preview":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Tasks
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckTasks(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "Priority":
                case "Status":
                case "PercentComplete":
                case "AssignedTo":
                case "Body":
                case "StartDate":
                case "DueDate":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Events
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckEvents(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "EventDate":
                case "EndDate":
                case "Description":
                case "Location":
                case "fRecurrence":
                case "WorkspaceLink":
                case "EventType":
                case "UID":
                case "RecurrenceID":
                case "EventCanceled":
                case "Duration":
                case "RecurrenceData":
                case "TimeZone":
                case "XMLTZone":
                case "MasterSeriesItemID":
                case "Workspace":
                case "fAllDayEvent":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Links
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckLinks(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "Comments":
                case "URLwMenu":
                case "URLNoMenu":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Announcements
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckAnnouncements(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "Expires":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Issues
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckIssues(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "Priority":
                case "Status":
                case "PercentComplete":
                case "AssignedTo":
                case "Comment":
                case "StartDate":
                case "DueDate":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Check if the field is a member of the content type Contacts
        /// </summary>
        /// <param name="fieldName">Name of field to check</param>
        /// <returns>True if the field is a default field</returns>
        private static bool CheckContacts(string fieldName)
        {
            bool returnValue = false;
            switch (fieldName)
            {
                case "LinkTitle":
                case "LinkTitleNoMenu":
                case "Title":
                case "LastNamePhonetic":
                case "FirstName":
                case "FirstNamePhonetic":
                case "FullName":
                case "Email":
                case "CompanyPhonetic":
                case "Company":
                case "JobTitle":
                case "WorkPhone":
                case "HomePhone":
                case "CellPhone":
                case "WorkFax":
                case "WorkAddress":
                case "WorkCity":
                case "WorkState":
                case "WorkZip":
                case "WorkCountry":
                case "WebPage":
                case "Comments":
                    {
                        returnValue = true;
                        break;
                    }
            }

            return returnValue;
        }

        /// <summary>
        /// Retrieves the document using Frontpage RPC
        /// </summary>
        /// <param name="url"></param>
        /// <param name="folderName"></param>
        /// <param name="fileName"></param>
        /// <param name="saveLocation"></param>
        /// <returns></returns>
        private static bool FpRpcGetDocument(string url, string folderName, string fileName, string saveLocation)
        {
            bool returnValue = true;
            Uri uri = new Uri(url);
            string method = "get document:6.0.2.5523";
            string serviceName = uri.AbsolutePath;
            string document = String.Empty;
            if (String.IsNullOrEmpty(folderName))
            {
                document = fileName;
            }
            else
            {
                document = folderName + "/" + fileName; 
            }

            string frontpageRPCCallString =
                "method={0}&service_name={1}&document_name={2}&old_theme_html=false&force=true&get_option=none&doc_version=&timeout=0";

            method = HttpUtility.UrlEncode(method);
            serviceName = HttpUtility.UrlEncode(serviceName);
            if (String.IsNullOrEmpty(folderName))
            {
                document = HttpUtility.UrlEncode(fileName);
            }
            else
            {
                document = HttpUtility.UrlEncode(String.Format(document, folderName + "/" + fileName)); 
            }

            frontpageRPCCallString = String.Format(frontpageRPCCallString, method, serviceName, document);

            frontpageRPCCallString.Replace(".", "%2e");
            frontpageRPCCallString.Replace("_", "%5f");
            byte[] fpRPCCall = System.Text.Encoding.UTF8.GetBytes(frontpageRPCCallString);
            byte[] data = new byte[fpRPCCall.Length + 1];
            fpRPCCall.CopyTo(data, 0);
            data[fpRPCCall.Length] = 0x0A;
            WebClient webClient = null;
            byte[] returnData = null;
            try
            {
                webClient = new WebClient();
                webClient.Credentials = System.Net.CredentialCache.DefaultCredentials;
                webClient.Headers.Add("Content-Type", "application/x-vermeer-urlencoded");
                webClient.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");
                returnData = webClient.UploadData(url + "/_vti_bin/_vti_aut/author.dll", "POST", data);

                // skip to the end of the vermeer packet - then grab everything else as the file content
                // find the end of the Vermeer packet by looking for the end of it's <html> block
                string returnStr = System.Text.Encoding.UTF8.GetString(returnData);
                int startPos = returnStr.IndexOf("</html>") + 8;
                int length = returnData.Length - startPos;
                byte[] contents = new byte[length];
                Array.Copy(returnData, startPos, contents, 0, length);

                using (StreamWriter sw = new StreamWriter(saveLocation, false))
                {
                    using (BinaryWriter bw = new BinaryWriter(sw.BaseStream))
                    {
                        bw.Write(contents);
                        bw.Flush();
                        sw.Flush();
                        sw.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ErrorLevel.Error, "Exception occurred: " + ex.Message);
                Logger.Log(ErrorLevel.Error, "Exception occurred: " + ex.StackTrace);
                returnValue = false;
            }
            finally
            {
                if (webClient != null)
                {
                    webClient.Dispose();
                }
            }

            return returnValue;
        }
    }
}
